/*
 *  Curl.cpp
 *  GRE
 *
 *  Created by Kutlak Roman on 19/10/2011.
 *  Copyright 2011 Kutlak Roman. All rights reserved.
 *
 */

#include <iostream>
#include <string>
#include <exception>

#include "Curl.h"
#include "Settings.h"

using std::string;
using std::cout;
using std::cerr;
using std::endl;

namespace kutlak {
namespace roman {
namespace gre {
    
    // class implemented
    /////////////////////////////// PUBLIC   ///////////////////////////////////
    
#pragma mark -
#pragma mark lifecycle
    size_t Curl::sActiveInstances = 0;
    size_t Curl::snRequests = 0;
    std::mutex Curl::sMutex;
    
    Curl::Curl() : mpCurl(0), mErrorCode(CURLE_OK), mErrorBuffer("")
    {
        Init();
    }
    
    
    Curl::~Curl()
    {
        std::lock_guard<std::mutex> lg(sMutex);
        curl_easy_cleanup(mpCurl);
        --sActiveInstances;
        if (0 == sActiveInstances)
            curl_global_cleanup();
    }
    
#pragma mark -
#pragma mark operations
    //=============================  OPERATIONS  ===============================
    
    string Curl::Request(const string& url)
    {
        if (url.empty())
            return "";
        
//#ifdef CURL_DEBUG
        cout << url << endl;
//#endif
        
        mResultBuffer.clear();
        mErrorBuffer[0] = '\0';
        
        curl_easy_setopt(mpCurl, CURLOPT_URL, url.c_str());
        
        mErrorCode = curl_easy_perform(mpCurl);
        
        if (CURLE_OK != mErrorCode)
            throw std::runtime_error(mErrorBuffer);

        ++snRequests;
        
        return mResultBuffer;
    }
    
    string Curl::Unescape(const string& str)
    {
        char* tmp;
        int len = -1;
        tmp = curl_easy_unescape(mpCurl, str.c_str(), str.size(), &len);
        
        string result(len, 0);
        if (tmp)
        {
            result = tmp;
            curl_free(tmp);
        }
        return result;
    }
#pragma mark -
#pragma mark inquiry
    //=============================  INQUIRY     ===============================
    
    const string& Curl::Result() const
    {
        return mResultBuffer;
    }
    
    string Curl::ErrorMessage() const
    {
        return mErrorBuffer;
    }
    
    CURLcode Curl::ErrorCode() const
    {
        return mErrorCode;
    }
    
    size_t Curl::Requests()
    {
        return snRequests;
    }
    
#pragma mark -
#pragma mark protected
    ///////////////////////////////  PROTECTED   ///////////////////////////////
    void Curl::Init()
    {
        std::lock_guard<std::mutex> lg(sMutex);
        if (0 == sActiveInstances)
        {
            CURLcode result = curl_global_init(CURL_GLOBAL_ALL);
            
            if (0 != result) // some error returned
                throw std::runtime_error("CURL could not perform global init.");
            
            ++sActiveInstances;
        }
        
        mpCurl = curl_easy_init();
        if (!mpCurl)
            throw std::runtime_error("CURL could not perform easy init.");
        
        // Now set up all of the curl options  
        curl_easy_setopt(mpCurl, CURLOPT_ERRORBUFFER, mErrorBuffer);  
        curl_easy_setopt(mpCurl, CURLOPT_HEADER, 0);  
        curl_easy_setopt(mpCurl, CURLOPT_FOLLOWLOCATION, 1);  
        curl_easy_setopt(mpCurl, CURLOPT_WRITEFUNCTION, &Curl::Writer);  
        curl_easy_setopt(mpCurl, CURLOPT_WRITEDATA, &mResultBuffer);
        
        Settings& s = Settings::Instance();
        if (s.GetSetting("UseProxy") == "true")
        {
            curl_easy_setopt(mpCurl, CURLOPT_PROXY,
                             s.GetSetting("http_proxy").c_str());
            curl_easy_setopt(mpCurl, CURLOPT_PROXYUSERNAME,
                             s.GetSetting("proxy_username").c_str());
            curl_easy_setopt(mpCurl, CURLOPT_PROXYPASSWORD,
                             s.GetSetting("proxy_password").c_str());
        }
    }
    
    int Curl::Writer(char *data, size_t size, size_t nmemb, string *buffer)  
    {  
        // What we will return  
        int bytes = size * nmemb;  
        
        // Is there anything in the buffer?  
        if (buffer != NULL)  
        {  
            // Append the data to the buffer  
            buffer->append(data, bytes);  
        }  
        
        return bytes;  
    }

    
} // end namespace gre
}
}
